AWS KMS S3バケットをSSE-KMSで暗号化しファイルのアップロード・ダウンロードするときにKMSへ必要なIAMポリシーを確認してみた
SSE-KMS暗号化したS3バケットに対してファイルのアップロード、ダウンロードに必要なIAMポリシーを考えていました。S3バケットへの操作権限と、KMSのキーポリシーがデフォルトの場合はSSE-KMS暗号化したS3バケットの場合はKMSへの操作権限も必要です。KMSに必要なIAMポリシーについて失敗した例を元にKMSのIAMポリシーに着目した覚書です。
書き残したかったこと
- ファイルのアップロードは暗号化するために
kms:Decrypt
と、kms:GenerateDataKey
の2つ許可が必要です。 - ファイルのダウンロードは復号するために
kms:Decrypt
の許可が必要です。
IAMポリシーサンプル
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:Decrypt", "kms:GenerateDataKey" ], "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03", "Effect": "Allow", "Sid": "SamplePolicy" }, { "Action": [ "s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::sample-dev-blog-bucket", "arn:aws:s3:::sample-dev-blog-bucket/*" ], "Effect": "Allow" } ] }
検証環境
EC2に設定したIAMロールのIAMポリシーのKMSに関するアクションを編集して、EC2からS3へのファイルアップロード・ダウンロードの動作を確認します。
CMKのキーポリシー
デフォルトのキーポリシー設定です。この場合はIAMポリシーで許可を与えられたアクションを許可されます。CMKのアクセス制御はIAMポリシーに寄せます。
{ "Version": "2012-10-17", "Id": "key-default-1", "Statement": [ { "Sid": "Enable IAM User Permissions", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789012:root" }, "Action": "kms:*", "Resource": "*" } ] }
EC2からS3のファイルをダウンロード
当初設定していたアップロードがダメなIAMポリシーです。失敗を記録することが目的なので順を追ってみていきます。ダウンロードに必要な権限を付与しています。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:Decrypt", "kms:Encrypt" ], "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03", "Effect": "Allow", "Sid": "SamplePolicy" }, { "Action": [ "s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::sample-dev-blog-bucket", "arn:aws:s3:::sample-dev-blog-bucket/*" ], "Effect": "Allow" } ] }
バケットの中身を一覧出力
S3バケットにはテストファイルが保存されています。
$ aws s3 ls s3://sample-dev-blog-bucket 2021-11-20 06:58:36 22 README.md
ファイルのダウンロード
S3からファイルをダウンロードします。
$ aws s3 cp s3://sample-dev-blog-bucket/README.md . download: s3://sample-dev-blog-bucket/README.md to ./README.md
EC2のローカルにコピーされたファイルを確認できました。kms:Decrypt
の許可があるためファイルを復号できます。
$ ll 合計 56 -rw-rw-r-- 1 ec2-user ec2-user 22 11月 20 06:58 README.md -rw-rw-r-- 1 ec2-user ec2-user 51573 11月 20 07:09 sample-file.png
一度脱線します。以下の様にkms:Decrypt
を許可しない(明示的な許可がない)場合はどうなるでしょうか?気になったので試してみます。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:Encrypt" ], "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03", "Effect": "Allow", "Sid": "SamplePolicy" }, { "Action": [ "s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::sample-dev-blog-bucket", "arn:aws:s3:::sample-dev-blog-bucket/*" ], "Effect": "Allow" } ] }
復号できないため失敗します。AccessDeniedのメッセージからですとS3の権限不足なのか、KMSの権限不足なのかは判断つかないですね。
$ aws s3 cp s3://sample-dev-blog-bucket/README.md . download failed: s3://sample-dev-blog-bucket/README.md to ./README.md An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
CloudTrailからログを確認します。イベントソースをkms.amazonaws.com
でフィルターすると探しやすいです。
エラーメッセージの項目にkms:Decrypt
の許可がないことを示したメッセージが載っていました。
{ "eventVersion": "1.08", "userIdentity": { "type": "AssumedRole", "principalId": "AROAQ4BT4DHFF4UKLZA33:i-06593b38f6016a363", "arn": "arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363", "accountId": "123456789012", "accessKeyId": "ASIAAAAAAAAAA", "sessionContext": { "sessionIssuer": { "type": "Role", "principalId": "AROAQ4BT4DHFF4UKLZA33", "arn": "arn:aws:iam::123456789012:role/dev-sample-ec2-role1", "accountId": "123456789012", "userName": "dev-sample-ec2-role1" }, "webIdFederationData": {}, "attributes": { "creationDate": "2021-11-21T04:45:45Z", "mfaAuthenticated": "false" }, "ec2RoleDelivery": "2.0" }, "invokedBy": "AWS Internal" }, "eventTime": "2021-11-21T05:25:32Z", "eventSource": "kms.amazonaws.com", "eventName": "Decrypt", "awsRegion": "ap-northeast-1", "sourceIPAddress": "AWS Internal", "userAgent": "AWS Internal", "errorCode": "AccessDenied", "errorMessage": "User: arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363 is not authorized to perform: kms:Decrypt on resource: arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03 because no resource-based policy allows the kms:Decrypt action", "requestParameters": null, "responseElements": null, "requestID": "b79ccf8e-51e7-48a3-a933-a56aacbb99db", "eventID": "ecfcb017-c7de-45d2-9bfc-2f8d94e260fe", "readOnly": true, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "123456789012", "eventCategory": "Management" }
エラーメッセージの親切仕様は2021年11月の以下のアップデートによるものだと思います。(こんなに親切だった記憶がないため)
EC2からS3へファイルのアップロード
当初に設定したIAMポリシーへ戻しました。ファイルのダウンロードの冒頭のIAMポリシーと同じです。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:Decrypt", "kms:Encrypt" ], "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03", "Effect": "Allow", "Sid": "SamplePolicy" }, { "Action": [ "s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::sample-dev-blog-bucket", "arn:aws:s3:::sample-dev-blog-bucket/*" ], "Effect": "Allow" } ] }
EC2からS3へファイルアップロード
ここからが本題です。EC2のローカルあるファイルをS3バケットへコピーしましたが失敗しました。暗号化するkms:Encrypt
があれば良いのでは?と深く考えず設定していました。
$ aws s3 cp ./sample-file.png s3://sample-dev-blog-bucket upload failed: ./sample-file.png to s3://sample-dev-blog-bucket/sample-file.png An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
CloudTrailからGenerateDataKeyのアクセス拒否されたログを確認できました。
because no resource-based policy allows the kms:GenerateDataKey action
とのことです。
{ "eventVersion": "1.08", "userIdentity": { "type": "AssumedRole", "principalId": "AROAQ4BT4DHFF4UKLZA33:i-06593b38f6016a363", "arn": "arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363", "accountId": "123456789012", "accessKeyId": "ASIAIOFEWWNFHDMBXTQQ", "sessionContext": { "sessionIssuer": { "type": "Role", "principalId": "AROAQ4BT4DHFF4UKLZA33", "arn": "arn:aws:iam::123456789012:role/dev-sample-ec2-role1", "accountId": "123456789012", "userName": "dev-sample-ec2-role1" }, "webIdFederationData": {}, "attributes": { "creationDate": "2021-11-21T05:30:17Z", "mfaAuthenticated": "false" }, "ec2RoleDelivery": "2.0" }, "invokedBy": "AWS Internal" }, "eventTime": "2021-11-21T05:39:05Z", "eventSource": "kms.amazonaws.com", "eventName": "GenerateDataKey", "awsRegion": "ap-northeast-1", "sourceIPAddress": "AWS Internal", "userAgent": "AWS Internal", "errorCode": "AccessDenied", "errorMessage": "User: arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363 is not authorized to perform: kms:GenerateDataKey on resource: arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03 because no resource-based policy allows the kms:GenerateDataKey action", "requestParameters": null, "responseElements": null, "requestID": "d315fec2-e9c1-46dd-a6a4-8ebc31505eea", "eventID": "13731da2-dcbb-4d43-9965-6ea61a864701", "readOnly": true, "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "123456789012", "eventCategory": "Management" }
データキーを生成してデータキーを使って暗号化するという基本的なことを失念していました。
アップロードに失敗した当初はググって以下のリンクを見て原因に気づきました。CloudTrailのログがわかりやすかったので説明と紹介の意味で後付けしています。
IAMポリシーを修正しkms:GenerateDataKey
の許可を追加しました。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "kms:Decrypt", "kms:Encrypt", "kms:GenerateDataKey" ], "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03", "Effect": "Allow", "Sid": "SamplePolicy" }, { "Action": [ "s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::sample-dev-blog-bucket", "arn:aws:s3:::sample-dev-blog-bucket/*" ], "Effect": "Allow" } ] }
改めてファイルをアップロードし成功します。
$ aws s3 cp ./sample-file.png s3://sample-dev-blog-bucket upload: ./sample-file.png to s3://sample-dev-blog-bucket/sample-file.png
S3バケットにコピーできたことを確認できました。ファイルのアップロードするにはファイルを暗号化するため、kms:Decrypt
とkms:GenerateDataKey
の許可が必要でした。
$ aws s3 ls s3://sample-dev-blog-bucket 2021-11-20 06:58:36 22 README.md 2021-11-21 06:05:10 51573 sample-file.png
まとめ
KMSに限れば以下のIAMポリシーの許可が必要でした。
- ファイルのアップロードは暗号化するために
kms:Decrypt
と、kms:GenerateDataKey
の2つ許可が必要です。 - ファイルのダウンロードは復号するために
kms:Decrypt
の許可が必要です。
その他、S3バケットへのs3:GetObject
やs3:PutObject
も必要です。
SSE-KMSで暗号化したS3バケットのファイル操作、KMSの権限については以下のリンクが勉強になりました。
CloudTrailからKMSのログを確認するときは、イベントソースをkms.amazonaws.com
でフィルターすると探しやすいです。
おわりに
AWSのセキュリティ試験で散々KMSを勉強していたのですが凡ミスをしました。自戒の意味を込めて失敗例に補足説明を加えて書き残しました。